﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Windows.Forms;
using SoftwareNinjas.BranchAndReviewTools.Core;
using SoftwareNinjas.BranchAndReviewTools.Gui.Extensions;
using SoftwareNinjas.BranchAndReviewTools.Gui.Grids;
using SoftwareNinjas.Core;
using SoftwareNinjas.BranchAndReviewTools.Gui.History;

namespace SoftwareNinjas.BranchAndReviewTools.Gui
{
    public partial class ChangeInspector : UserControl, IHistoryItem
    {
        private object _context;
        private bool _restoredSplitters;

        public ChangeInspector()
        {
            InitializeComponent();
            this.Paint += OnPaint;
            ChangeLog.InitializeDefaults();
            PatchViewer.InitializeDefaults();
            PatchViewer.InitializeDiff();
            PatchViewer.KeyUp += PatchViewer_KeyUp;
            FileGrid.Grid.MultiSelect = true;
        }

        private void OnPaint(object sender, PaintEventArgs paintEventArgs)
        {
            if (!_restoredSplitters)
            {
                HorizontalDividerSplitterDistance = this.LoadSetting(() => HorizontalDividerSplitterDistance, 85);
                VerticalDividerSplitterDistance = this.LoadSetting(() => VerticalDividerSplitterDistance, 273);

                this.horizontalDivider.SplitterMoved += this.horizontalDivider_SplitterMoved;
                this.verticalDivider.SplitterMoved += this.verticalDivider_SplitterMoved;

                _restoredSplitters = true;
            }
        }

        void PatchViewer_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyValue == 107 || e.KeyValue == 109)
            {
                PatchViewer.NativeInterface.ToggleFold(PatchViewer.GetCurrentLineNumber());
            }
        }

        [SettingsBindable(true)]
        public int VerticalDividerSplitterDistance
        {
            get { return verticalDivider.SplitterDistance; }
            set { verticalDivider.SplitterDistance = value; }
        }

        [SettingsBindable(true)]
        public int HorizontalDividerSplitterDistance
        {
            get { return horizontalDivider.SplitterDistance; }
            set { horizontalDivider.SplitterDistance = value; }
        }

        public string PatchText
        {
            get { return PatchViewer.Text; }
            set { PatchViewer.SetReadOnlyText(value); }
        }

        public object Context {
            get { return _context; }
            set
            { 
                _context = value;
                RefreshMessage();
                RefreshFileGrid();
            }
        }

        // given a Context (the current BranchId in the "Pending Changes" case and the current revision in "Log"),
        // produces a table of the changes involved
        public Func<object, DataTable> ChangesFunction { get; set; }

        // given one or more "ID"s from the table generated by ChangesFunction, produces a patch
        public Func<object, IEnumerable<object>, string> ComputeDifferencesFunction { get; set; }

        public Func<object, IEnumerable<object>, IList<MenuAction>> ActionsForChangesFunction { get; set; }

        public Func<object, string> MessageFunction { get; set; }

        public void RefreshMessage()
        {
            if (Context != null && MessageFunction != null)
            {
                this.StartTask(() => MessageFunction(Context), UpdateMessage);
            }
        }

        private void UpdateMessage(string message)
        {
            if (message != null)
            {
                if (this.ChangeLog.IsReadOnly)
                {    
                    this.ChangeLog.SetReadOnlyText(message);
                }
                else
                {
                    this.ChangeLog.Text = message;
                }
            }
        }

        #region FileGrid
        public void RefreshFileGrid()
        {
            if (Context != null && ChangesFunction != null)
            {
                this.StartTask(() => ChangesFunction(Context), LoadFileGrid);
            }
        }

        private void LoadFileGrid(DataTable dataTable)
        {
            FileGrid.DataTable = dataTable;
            var itemCount = FileGrid.Grid.Rows.Count;
            if (0 == itemCount)
            {
                PatchText = String.Empty;
            }
            FileGrid.Caption = "{0} changed item{1}".FormatInvariant(itemCount, itemCount == 1 ? "" : "s");
        }

        void FileGrid_ContextMenuStripNeeded(object sender, ContextMenuNeededEventArgs e)
        {
            var menu = BuildChangedFilesActionMenu();
            e.ContextMenu = menu;
        }

        void FileGrid_SelectionChanged(object sender, EventArgs e)
        {
            if (Context != null && ComputeDifferencesFunction != null)
            {
                var selectedIds = FileGrid.FindSelectedIds().ToList();
                this.StartTask(() => ComputeDifferencesFunction(Context, selectedIds), UpdatePatchText);
            }
        }

        private void UpdatePatchText(string patchText)
        {
            PatchText = patchText;
        }

        private ContextMenu BuildChangedFilesActionMenu()
        {
            var menu = new ContextMenu();
            if (Context != null && ActionsForChangesFunction != null)
            {
                var selectedIds = FileGrid.FindSelectedIds();
                var actions = ActionsForChangesFunction(Context, selectedIds);
                menu.MenuItems.AddActions(actions);
            }
            return menu;
        }

        private void InvokeDefaultChangedFilesAction()
        {
            var menu = BuildChangedFilesActionMenu();
            menu.MenuItems.InvokeFirstMenuItem();
        }

        void FileGrid_RowInvoked(object sender, EventArgs e)
        {
            InvokeDefaultChangedFilesAction();
        }
        #endregion

        private void horizontalDivider_SplitterMoved(object sender, SplitterEventArgs e)
        {
            this.SaveSetting(() => HorizontalDividerSplitterDistance);
        }

        private void verticalDivider_SplitterMoved(object sender, SplitterEventArgs e)
        {
            this.SaveSetting(() => VerticalDividerSplitterDistance);
        }

        #region IHistoryItem Members

        public new IHistoryContainer Container { get; set; }

        Control IHistoryItem.Control { get { return this; } }

        public string Title { get; set; }

        public void Reload()
        {
            RefreshFileGrid();
        }

        #endregion
    }
}
